import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;

/**
 * @author Daniel Bruzual
 * @author Sabrina Fernandez
 * @author Manuel Gomez
 * @author Marilyn Nowacka
 */
public class ManejadorInstruccion {

	TablaDePagina tabla;
	
	//Lista ordenada por vejez, contiene indices de memoria
	//Sirve para hacer LRU
	LinkedList<Integer> accedidas;
	
	//La ram como tal, son los marcos de pagina
	ArrayList<Pagina> memoria;
	
	//Para llevar estadisticas
	int hits = 0;
	int miss = 0;

	public ManejadorInstruccion( int num_marcos){
		this.tabla = new TablaDePagina();
		accedidas = new LinkedList<Integer>();
		memoria = new ArrayList<Pagina>(num_marcos);

		for(int i=0; i<num_marcos; i++){
			accedidas.addLast(i);
			memoria.add(null);
		}
	}
	
	public int ejecutaInstruccion(Instruccion ins){
		int res = 0;
		switch (ins.getTipo()){

		case 'r':
			res = read(ins.getEjecutor(), ins.getDir_mem());
			break;
		case 'w':
			res = write(ins.getEjecutor(), ins.getDir_mem());
			break;
		}
		return res;
	}

	public int read(Proceso p, int direccion){
		int direccion_real = p.getCr3() + direccion;

		int pos = 0;
		if (tabla.hayFalloPagina(direccion_real)){ //No la tengo
			pos = LRU(tabla.cargarPagina(p, direccion_real));
			miss++;
		} else {
			pos = posicion_accedida(direccion_real);
			hits++;
		}
		//Actualizar accedidas
		//buscar en "accedidas" la direccion_pag y moverla al final de la lista (push).
		actualizarAccedidas(pos);
		return pos;
	}

	public int write(Proceso p, int direccion){
		int direccion_real = p.getCr3() + direccion;
		int pos = 0;
		if (tabla.hayFalloPagina(direccion_real)){ //No la tengo
			//Hacer LRU. El devuelve la pagina a eliminar
			pos = LRU(tabla.cargarPagina(p, direccion_real));
			miss++;
		}  else {
			pos = posicion_accedida(direccion_real);
			hits++;
		}
		//Escribir (actualizar dirty)
		tabla.getPagina(direccion_real).setDirty(true);

		//Actualizar accedidas
		//buscar en "accedidas" la direccion_pag y moverla al final de la lista (push).
		actualizarAccedidas(pos);
		return pos;
	}

	public int posicion_accedida(int dir){
		Iterator<Integer> it = accedidas.descendingIterator();
		int pos = 0;
		int dir_pag = (dir/4096)*4096;
		while(it.hasNext()){
			pos = it.next();
			if(memoria.get(pos)!=null && memoria.get(pos).getDir_inicio() == dir_pag){
				return pos;
			}
		}
		return 0;
	}

	public int LRU(Pagina pag_nueva){
		Iterator<Integer> it = accedidas.iterator();
		Pagina pag = null;
		int pos = 0;
		while(it.hasNext()){
			pos = it.next();
			pag = memoria.get(pos);
			if(pag == null){
				break;
			}
			if(!pag.isDirty()){
				break;
			}
		}
		if (pag != null && pag.isDirty()){
			pos = accedidas.getFirst();
			pag = memoria.get(pos);
		}
		if (pag != null){
			tabla.eliminarPagina(pag.getDir_inicio());
		}

		memoria.set(pos, pag_nueva);
		return pos;
	}

	public void actualizarAccedidas(int pos){
		int mas_reciente = accedidas.indexOf(pos);
		accedidas.remove(mas_reciente);
		accedidas.addLast(pos);

	}

	public void imprimir(){
		Iterator<Pagina> it = memoria.iterator();
		int i = 0;
		while(it.hasNext()){
			System.out.print("\t"+i*4+"Kb ");
			Pagina marco = it.next();
			if(marco == null){
				System.out.print("\t[  ]\n");
			} else {
				System.out.print("\t["+marco.getOwner().getColor()+"]"+(marco.isDirty()? "*" : "" )+"\n");
			}
			i++;
		}
	}

	public int getHits(){
		return this.hits;
	}
	public int getMiss(){
		return this.miss;
	}
}
